///
/// @Author: YoungChan
///LastEditors: YoungChan
/// @Description: 网络请求封装
/// @Date: 2019-02-19 18:25:27
///LastEditTime: 2020-11-18 11:13:30
///
import 'dart:io';

import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';

import '../utils/nd_cache.dart';
import '../utils/nd_event_bus.dart';

enum NCRequestMethod { get, post, put, delete }

///日志回调
/// level: 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR
typedef NCRequestLogger = Function(String tag, int level, String message);

Api get api => Api.shareInstance();

class Api {
  static final cacheGroup = "ndApi";
  static Api? _instance;
  static String? _host;
  static String? _proxy;

  String? get proxy => _proxy;

  String? get host => _host;

  static Api shareInstance() => _instance ??= Api();

  Dio? _dio;
  LogInterceptor? _logInterceptor;
  CookieManager? _cookieManager;
  NCRequestLogger? _logger;

  /// 接口请求header
  Map<String, String> _headers = {
    'Accept': 'application/json',
    'token': '82b53fc2ce0a43878671f782567dd1a7',
  };

  ///  新增header信息   PS: header字段不可以有中文 否则会报错
  void addHeader(Map<String, String> headers) {
    _headers.addAll(headers);
    if (_dio != null) {
      _dio!.options.headers = _headers;
    }
  }

  Map<String, String> getHeaders() {
    return _headers;
  }

  ///获取cookie
  Future<List<Map<String, String>>?> getCookie() async {
    if (_cookieManager != null && _host != null) {
      var cookies =
          await _cookieManager!.cookieJar.loadForRequest(Uri.parse(_host!));
      return Future.value(
          cookies.map((cookie) => {cookie.name: cookie.value}).toList());
    } else {
      return null;
    }
  }

  ///设置Cookie
  ///调用之前，保证先调用setHost
  Future setCookie(Map? cookie) async {
    if (kIsWeb) {
      return;
    }
    if (_host == null) {
      return Future.value();
    }

    var cookieList = <Cookie>[];
    cookie!.forEach((key, value) {
      cookieList.add(Cookie(key, value));
    });
    if (_cookieManager != null) {
      if (_dio!.interceptors.contains(_cookieManager)) {
        _dio!.interceptors.remove(_cookieManager);
      }
    }
    var appDir = await getApplicationDocumentsDirectory();
    var cj = PersistCookieJar(storage: FileStorage(appDir.path));
    cj.saveFromResponse(Uri.parse(_host!), cookieList);
    _cookieManager = CookieManager(cj);
    _dio!.interceptors.add(_cookieManager!);
    return Future.value();
  }

  void setLogger(NCRequestLogger logger) {
    _logger = logger;
  }

  ///设置Host
  Future setHost(String host) async {
    _host = host;
    NDCache.setString(cacheGroup, "host", host);
    Map<String, dynamic>? extra;
    if (kIsWeb) {
      extra = {'withCredentials': true};
    }
    var options = BaseOptions(
        baseUrl: host,
        connectTimeout: const Duration(milliseconds: 8000),
        receiveTimeout: const Duration(milliseconds: 15000),
        responseType: ResponseType.json,
        extra: extra,
        headers: _headers);

    _dio = Dio(options);

    if (kIsWeb) {
      return Future.value();
    }
    if (_cookieManager == null) {
      var appDir = await getApplicationDocumentsDirectory();
      var cj = PersistCookieJar(storage: FileStorage(appDir.path));
      _cookieManager = CookieManager(cj);
    }
    _dio!.interceptors.add(_cookieManager!);

    // /// 增加网络请求耗时监听
    // _dio!.interceptors.add(TimeConsumingInterceptor()
    //   ..onTimeCallBack = (path, message, times) {
    //     NcImproveKit.instance?.eventBus
    //         .fire(ImproveRequestModel(DateTime.now(), message, path, times));
    //   });
    if (_proxy?.isNotEmpty == true) {
      setProxy(proxy: _proxy);
    }
    return Future.value();
  }

  ///示例proxy： 10.0.0.1:8888
  void setProxy({String? proxy}) {
    _proxy = proxy;
    // if (bool.fromEnvironment("dart.vm.product") || _dio == null) {
    //   return;
    // }
    if (_proxy?.isNotEmpty == true) {
      (_dio!.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
          (client) {
        // config the http client
        client.findProxy = (uri) {
          //proxy all request to localhost:8888
          return "PROXY $proxy";
        };
        client.badCertificateCallback = (cert, host, port) => true;
        // you can also create a new HttpClient to dio
        // return new HttpClient();
      };
    } else {
      (_dio!.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
          (client) {
        client.badCertificateCallback = (cert, host, port) => true;
        // you can also create a new HttpClient to dio
        // return new HttpClient();
      };
    }
  }

  void setDebug(bool debug) {
    if (_dio != null) {
      if (debug) {
        //调试状态下开启日志
        // if (_logInterceptor == null) {
        //   _logInterceptor =
        //       LogInterceptor(requestBody: true, responseBody: true);
        //   _logInterceptor!.logPrint = (obj) {
        //     NcImproveKit.instance?.eventBus
        //         .fire(ImproveLogModel(DateTime.now(), obj.toString()));
        //   };
        // }
        if (!_dio!.interceptors.contains(_logInterceptor)) {
          _dio!.interceptors.add(_logInterceptor!);
        }
      } else {
        if (_dio!.interceptors.contains(_logInterceptor)) {
          _dio!.interceptors.remove(_logInterceptor);
        }
      }
    }
  }

  ///设置授权失败/登录过期的监听 401错误码
  void setOnAuthInvalidListener(void Function(String?) listener) {
    NDEventBus.singleton.subscribe("authInvalid").listen((value) {
      print("===> Receive auth failed event!!");
      listener(value?.toString());
    });
  }

  ///设置登录冲突监听 501错误码
  void setOnAuthConflictListener(void Function(String?) listener) {
    NDEventBus.singleton.subscribe("authConflict").listen((value) {
      print("===> Receive auth failed event!!  authConflict");
      listener(value?.toString());
    });
  }

  ///设置密码有效期过期的监听 403001错误码
  void setOnPasswordFirstConflictListener(void Function(String?) listener) {
    NDEventBus.singleton
        .subscribe("modifyPasswordFirstConflict")
        .listen((value) {
      listener(value?.toString());
    });
  }

  ///设置密码有效期过期的监听 403002错误码
  void setOnPasswordConflictListener(void Function(String?) listener) {
    NDEventBus.singleton.subscribe("modifyPasswordConflict").listen((value) {
      listener(value?.toString());
    });
  }

  ///设置无权限监听 402错误码
  void setOnUnauthorizedListener(void Function(String?) listener) {
    NDEventBus.singleton.subscribe("unauthorized").listen((value) {
      print("===> Receive unauthorized failed event!!");
      listener(value?.toString());
    });
  }

  Future<OMResponse> _handleDioError<T>(DioError e) {
    int? code = -1;
    var msg = '抱歉！请求出错~';
    var isCanceled = false;
    switch (e.type) {
      case DioExceptionType.cancel:
        msg = '请求取消';
        isCanceled = true;
        break;
      case DioExceptionType.sendTimeout:
      case DioExceptionType.connectionTimeout:
        msg = '网络连接超时';
        break;
      case DioExceptionType.receiveTimeout:
        msg = '数据接收超时';
        break;
      case DioExceptionType.badCertificate:
        msg = '证书无效badCertificate';
        break;
      case DioExceptionType.connectionError:
        msg = '连接错误connectionError';
        break;
      case DioExceptionType.badResponse:
        if (e.response != null) {
          code = e.response!.statusCode;
          msg = e.response!.statusMessage ?? 'HttpStatusCode：$code 未知错误';
          if (e.response?.data?.isNotEmpty == true && e.response?.data is Map) {
            var ret = e.response?.data['ret'];
            code = e.response?.data['code'] as int?;
            if (ret != null) {
              msg = e.response!.data['errorMsg'] ?? msg;
              code = e.response!.data['errorCode'];
            } else if (code != null) {
              msg = e.response!.data['message'] ?? msg;
              var cpsErrorCode = e.response!.data['cpsErrorCode'] as String?;
              msg = '${cpsErrorCode != null ? '$cpsErrorCode：' : ''}$msg';
            }
            //授权失败,登录过期
            if (code == 401) {
              NDEventBus.singleton.post("authInvalid", msg);
            } else if (code == 501) {
              NDEventBus.singleton.post("authConflict", msg);
            } else if (code == 402) {
              NDEventBus.singleton.post("unauthorized", msg);
            } else {
              _logger?.call('NCRequest', 4,
                  'url=${e.requestOptions.uri.toString()}, method=${e.requestOptions.method}, parameters=${e.requestOptions.queryParameters.toString()}, data=${e.requestOptions.data?.toString()}, responseCode=${e.response?.statusCode}, responseMsg=${e.response?.statusMessage}, responseData=${e.response?.data?.toString()}');
            }
          }
        }
        break;
      default:
        msg = '网络异常';
        break;
    }
    return Future.value(OMResponse(
      code: code,
      message: msg,
      isCanceled: isCanceled,
    ));
  }

  // ///移除null 数据
  // void _removeNullValueOfMap(Map? data) {
  //   var removes = [];
  //
  //   data?.forEach((key, value) {
  //     if (value != null && value is List) {
  //       _removeNullValueOfList(value);
  //       // if (value.isEmpty) {
  //       //   removes.add(value);
  //       // }
  //     } else if (value != null && value is Map) {
  //       _removeNullValueOfMap(value);
  //       // if (value.isEmpty) {
  //       //   removes.add(value);
  //       // }
  //     } else if (value == null) {
  //       removes.add(value);
  //     }
  //   });
  //   data?.removeWhere((key, value) {
  //     return removes.contains(value);
  //   });
  // }
  //
  // ///移除null 数据
  // void _removeNullValueOfList(List data) {
  //   var removes = [];
  //   data.forEach((item) {
  //     if (item != null && item is Map) {
  //       _removeNullValueOfMap(item);
  //       // if (l.isEmpty) {
  //       //   removes.add(l);
  //       // }
  //     } else if (item != null && item is List) {
  //       _removeNullValueOfList(item);
  //     } else if (item == null) {
  //       removes.add(item);
  //     }
  //   });
  //   data.removeWhere((d) {
  //     return removes.contains(d);
  //   });
  // }
  Future<OMResponse> get(String apiPath,
      {Map<String, dynamic>? queryParams,
      NCRequestType requestType = NCRequestType.JSON,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken}) {
    return request(
      apiPath,
      NCRequestMethod.post,
      queryParams: queryParams,
      requestType: requestType,
      headers: headers,
      cancelToken: cancelToken,
    );
  }

  Future<OMResponse> post(String apiPath,
      {dynamic data,
      NCRequestType requestType = NCRequestType.JSON,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken}) {
    return request(
      apiPath,
      NCRequestMethod.post,
      data: data,
      requestType: requestType,
      headers: headers,
      cancelToken: cancelToken,
    );
  }

  Future<OMResponse> request(String path, NCRequestMethod method,
      {Map<String, dynamic>? queryParams,
      dynamic data,
      NCRequestType requestType = NCRequestType.JSON,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken}) async {
    if (_dio == null) {
      return Future.error("Uninitial request");
    }
    var connResult = await Connectivity().checkConnectivity();
    if (connResult == ConnectivityResult.none) {
      return Future.value(OMResponse(code: -100, message: "当前无网络，请检查设备网络连接"));
    }
    try {
      Response res;
      // if (queryParams?.isNotEmpty == true) {
      //   _removeNullValueOfMap(queryParams);
      // }
      // if (data != null) {
      //   if (data is Map) {
      //     _removeNullValueOfMap(data);
      //   } else if (data is List) {
      //     _removeNullValueOfList(data);
      //   }
      // }
      var options = Options(headers: headers);
      if (method != NCRequestMethod.get) {
        switch (requestType) {
          case NCRequestType.FORM:
            options.contentType = "application/x-www-form-urlencoded";
            break;
          case NCRequestType.JSON:
            options.contentType = "application/json";
            break;
          case NCRequestType.TEXT:
            options.contentType = "application/text";
            break;
        }
      }
      switch (method) {
        case NCRequestMethod.get:
          res = await _dio!.get<Map<String, dynamic>>(path,
              queryParameters: queryParams,
              cancelToken: cancelToken,
              options: options);
          break;
        case NCRequestMethod.delete:
          res = await _dio!.delete<Map<String, dynamic>>(path,
              queryParameters: queryParams,
              data: data,
              cancelToken: cancelToken,
              options: options);
          break;
        case NCRequestMethod.post:
          res = await _dio!.post<Map<String, dynamic>>(path,
              queryParameters: queryParams,
              data: data,
              cancelToken: cancelToken,
              options: options);
          break;
        case NCRequestMethod.put:
          res = await _dio!.put<Map<String, dynamic>>(path,
              queryParameters: queryParams,
              data: data,
              cancelToken: cancelToken,
              options: options);
          break;
        default:
          return Future.error("Method $method not support");
      }
      print('res.statusCode:${res.statusCode}  res.data:${res.data}');

      return Future.value(OMResponse(code: 200, data: res.data));
    } on DioError catch (e) {
      return _handleDioError(e);
    }
  }
}

class OMResponse {
  int? code;
  String? message;
  dynamic data;
  dynamic otherInfo;
  bool isCanceled;

  OMResponse(
      {this.code,
      this.message,
      this.isCanceled = false,
      this.data,
      this.otherInfo});
}

enum NCRequestType {
  JSON,
  FORM,
  TEXT,
}
